package cm.util;

import cm.annotation.ExcelDataSource;
import cn.hutool.core.lang.Pair;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * @author yxh
 * @description
 * @date Created in 2025/10/23 17:11
 */
public class ExcelTools {
    private static final Logger log = LoggerFactory.getLogger(ExcelTools.class);
    private static final String SOURCE = "{source}";
    private static final String DEST = "{dest}";

    public ExcelTools() {
    }

    public static void export(HttpServletResponse response, String filePrefix, List... lists) {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String exportFileName = filePrefix + "-" + LocalDateTime.now().toString("YYYYMMDDhhmmss") + ".xlsx";
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(exportFileName, StandardCharsets.UTF_8));
        ExcelWriter excelWriter = null;

        try {
            excelWriter = EasyExcel.write(response.getOutputStream()).build();

            for(int i = 0; i < lists.length; ++i) {
                List list = lists[i];
                if (!CollectionUtils.isEmpty(list)) {
                    Object firstItem = list.get(0);
                    Class<?> itemClass = firstItem.getClass();
                    WriteSheet writeSheet = ((ExcelWriterSheetBuilder)((ExcelWriterSheetBuilder)EasyExcel.writerSheet(i + 1, "sheet" + (i + 1)).head(itemClass)).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())).build();
                    excelWriter.write(list, writeSheet);
                }
            }
        } catch (IOException var13) {
            log.error(var13.getMessage(), var13);
            throw new RuntimeException("导出Excel失败: " + var13.getMessage());
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }

        }

    }

    public static String[] getExcelPropertyValue(Field field) {
        ExcelProperty excelProperty = (ExcelProperty)field.getAnnotation(ExcelProperty.class);
        return excelProperty != null ? excelProperty.value() : null;
    }

    public static String getFirstExcelPropertyValue(Field field) {
        String[] values = getExcelPropertyValue(field);
        return values != null && values.length > 0 ? values[0] : null;
    }

    public static Integer getColumnWidthValue(Field field) {
        ColumnWidth columnWidth = (ColumnWidth)field.getAnnotation(ColumnWidth.class);
        return columnWidth != null ? columnWidth.value() : 10;
    }

    public static <T> File createOneSheetExcel(List<T> dataList, Class<T> cls) throws IOException {
        return createOneSheetExcel("Sheet1", dataList, cls);
    }

    public static <T> File createOneSheetExcel(String sheetName, List<T> dataList, Class<T> cls) throws IOException {
        if (Objects.isNull(dataList)) {
            return null;
        } else {
            Workbook workbook = new XSSFWorkbook();
            warpSheetWithData(sheetName, dataList, cls, workbook);
            File tempFile = File.createTempFile(UUID.randomUUID().toString(), ".xlsx");
            FileOutputStream outputStream = new FileOutputStream(tempFile);

            try {
                workbook.write(outputStream);
            } catch (Throwable var9) {
                try {
                    outputStream.close();
                } catch (Throwable var8) {
                    var9.addSuppressed(var8);
                }

                throw var9;
            }

            outputStream.close();
            workbook.close();
            return tempFile;
        }
    }

    public static <T> File createMultiSheetsExcel(List<Pair> dataSheetList) throws IOException {
        if (Objects.isNull(dataSheetList)) {
            return null;
        } else {
            Workbook workbook = new XSSFWorkbook();
            String tableCnName = null;
            Iterator var3 = dataSheetList.iterator();

            while(var3.hasNext()) {
                Pair pair = (Pair)var3.next();
                String sheetName = (String)pair.getKey();
                List<T> dataList = (List)pair.getValue();
                if (CollectionUtils.isEmpty(dataList)) {
                    log.warn("数据列表为空，跳过创建工作表: {}", sheetName);
                } else {
                    T dataItem = dataList.get(0);
                    Class cls;
//                    if (dataItem instanceof BeanDiff) {
//                        cls = ((BeanDiff)dataList.get(0)).getClassType();
//                        wrapSheetWithDiff(sheetName, dataList, cls, workbook);
//                    } else {
//                        cls = dataItem.getClass();
//                        warpSheetWithData(sheetName, dataList, cls, workbook);
//                    }

                    if (Objects.isNull(tableCnName)) {
                        tableCnName = ((ExcelDataSource)cls.getAnnotation(ExcelDataSource.class)).tableCnName();
                    }
                }
            }

            File file = newFile(tableCnName);
            FileOutputStream outputStream = new FileOutputStream(file);

            try {
                workbook.write(outputStream);
            } catch (Throwable var11) {
                try {
                    outputStream.close();
                } catch (Throwable var10) {
                    var11.addSuppressed(var10);
                }

                throw var11;
            }

            outputStream.close();
            workbook.close();
            return file;
        }
    }

    private static File newFile(String dataType) throws IOException {
        String prefix = dataType + "-" + DateTools.getCurrentDateTimeShort();
        String originalName = prefix + ".xlsx";
        File targetFile = new File(System.getProperty("java.io.tmpdir"), originalName);
        Files.createDirectories(targetFile.getParentFile().toPath());
        if (targetFile.exists() && !targetFile.delete()) {
            throw new IOException("无法删除已存在的文件: " + targetFile.getAbsolutePath());
        } else if (targetFile.createNewFile()) {
            return targetFile;
        } else {
            throw new IOException("无法创建文件: " + targetFile.getAbsolutePath());
        }
    }

    private static <T> void warpSheetWithData(String sheetName, List<T> dataList, Class<T> cls, Workbook workbook) {
        if (!Objects.isNull(dataList) && !dataList.isEmpty()) {
            Sheet sheet = workbook.createSheet(sheetName);
            Row headerRow = sheet.createRow(0);
            List<String> fieldNames = ReflectTools.getFields(cls);

            int i;
            for(i = 0; i < fieldNames.size(); ++i) {
                String fieldName = (String)fieldNames.get(i);
                Field field = ReflectTools.getDeclaredField(cls, fieldName);
                String columnName = getFirstExcelPropertyValue(field);
                headerRow.createCell(i).setCellValue(columnName);
                Integer columnWidth = getColumnWidthValue(field);
                sheet.setColumnWidth(i, columnWidth * 256);
            }

            for(i = 0; i < dataList.size(); ++i) {
                Row row = sheet.createRow(i + 1);
                Object obj = dataList.get(i);

                for(int j = 0; j < fieldNames.size(); ++j) {
                    String fieldName = (String)fieldNames.get(j);
                    Cell cell = row.createCell(j);
                    Object fieldValue = ReflectTools.getFieldValue(obj, fieldName);
                    setCellValue(cell, fieldValue);
                }
            }

        }
    }

    private static <T> void wrapSheetWithDiff(String sheetName, List<BeanDiff<T>> beanDiffList, Class<T> cls, Workbook workbook) {
        if (!CollectionUtils.isEmpty(beanDiffList)) {
            String dataType = ((ExcelDataSource)cls.getAnnotation(ExcelDataSource.class)).tableCnName();
            Sheet sheet = workbook.createSheet(sheetName);
            Row headerRow = sheet.createRow(0);
            String[] sourceNames = getSourceDestNames(cls);
            String sourceName = sourceNames[0];
            String destName = sourceNames[1];
            headerRow.createCell(0).setCellValue(sourceName + "明细");
            headerRow.createCell(1).setCellValue(destName + "明细");
            sheet.setColumnWidth(0, 7680);
            sheet.setColumnWidth(1, 7680);

            for(int i = 0; i < beanDiffList.size(); ++i) {
                BeanDiff<T> beanDiff = (BeanDiff)beanDiffList.get(i);
                Row row = sheet.createRow(i + 1);
                row.createCell(0).setCellValue(JSON.toJSONString(beanDiff.getSourceRecord()));
                row.createCell(1).setCellValue(JSON.toJSONString(beanDiff.getDestRecord()));
                Integer columnIndex = 1;
                List<FieldDiff> diffFieldList = beanDiff.getDiffFieldList();

                for(int j = 0; j < diffFieldList.size(); ++j) {
                    FieldDiff fieldDiff = (FieldDiff)diffFieldList.get(j);
                    String fieldEnName = ReflectTools.getFieldValueStr(fieldDiff, "fieldEnName");
                    String fieldCnName = ReflectTools.getFieldValueStr(fieldDiff, "fieldCnName");
                    String sourceValue = ReflectTools.getFieldValueStr(fieldDiff, "sourceValue");
                    String destValue = ReflectTools.getFieldValueStr(fieldDiff, "destValue");
                    String fieldCellValue = String.format("【%s】%s", fieldCnName, fieldEnName);
                    String sourceCellValue = String.format("【%s】%s", sourceName, sourceValue);
                    String destCellValue = String.format("【%s】%s", destName, destValue);
                    row.createCell(columnIndex = columnIndex + 1).setCellValue(fieldCellValue);
                    sheet.setColumnWidth(columnIndex, (fieldCellValue.length() + 5) * 256);
                    row.createCell(columnIndex = columnIndex + 1).setCellValue(sourceCellValue);
                    sheet.setColumnWidth(columnIndex, (sourceCellValue.length() + 5) * 256);
                    row.createCell(columnIndex = columnIndex + 1).setCellValue(destCellValue);
                    sheet.setColumnWidth(columnIndex, (destCellValue.length() + 5) * 256);
                }
            }

        }
    }

    public static <T> String[] getSourceDestNames(Class<T> cls) {
        String[] sourceNames = ((ExcelDataSource)cls.getAnnotation(ExcelDataSource.class)).sourceType();
        if (sourceNames != null && sourceNames.length >= 2) {
            return sourceNames;
        } else {
            throw new IllegalArgumentException("ExcelDataSource annotation must have at least two source names");
        }
    }

    private static void setCellValue(Cell cell, Object value) {
        if (value == null) {
            cell.setCellValue("");
        } else if (value instanceof Number) {
            cell.setCellValue(((Number)value).doubleValue());
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean)value);
        } else {
            cell.setCellValue(value.toString());
        }

    }
}
